<?php

namespace JingdongApi;

use JingdongApi\Models\ApiConfig;
use JingdongApi\Models\ApiResult;
use JingdongApi\Models\OrderData;
use JingdongApi\Models\MerchantInfo;

/**
 * 京东配送API SDK
 * 
 * @package JingdongApi
 * @version 1.0.0
 * @author 林生
 */
class JingdongApiSDK
{
    /**
     * 版本号
     */
    const VERSION = '1.0.0';
    
    /**
     * API地址配置
     */
    const API_BASE_TEST = 'https://api-test.hiboos.com';
    const API_BASE_SANDBOX = 'https://api-sandbox.hiboos.com';
    const API_BASE_PROD = 'https://api.hiboos.com';
    
    /**
     * 配置对象
     * @var ApiConfig
     */
    private $config;
    
    /**
     * 日志回调函数
     * @var callable
     */
    private $logger;
    
    /**
     * 构造函数
     * 
     * @param array|ApiConfig $config 配置信息
     */
    public function __construct($config)
    {
        if (is_array($config)) {
            $this->config = new ApiConfig($config);
        } elseif ($config instanceof ApiConfig) {
            $this->config = $config;
        } else {
            throw new \InvalidArgumentException('配置参数类型错误');
        }
        
        $this->config->validate();
    }
    
    /**
     * 设置日志回调函数
     * 
     * @param callable $logger 日志回调函数，接收参数：($level, $message, $context)
     */
    public function setLogger($logger)
    {
        if (!is_callable($logger)) {
            throw new \InvalidArgumentException('日志回调函数必须是可调用的');
        }
        $this->logger = $logger;
    }
    
    /**
     * 记录日志
     * 
     * @param string $level 日志级别
     * @param string $message 日志消息
     * @param array $context 上下文信息
     */
    private function log($level, $message, $context = [])
    {
        if (!$this->config->enableLog) {
            return;
        }
        
        if ($this->logger && is_callable($this->logger)) {
            call_user_func($this->logger, $level, $message, $context);
        } else {
            // 默认输出到error_log
            $logMessage = sprintf('[%s] [%s] %s', date('Y-m-d H:i:s'), strtoupper($level), $message);
            if (!empty($context)) {
                $logMessage .= ' ' . json_encode($context, JSON_UNESCAPED_UNICODE);
            }
            error_log($logMessage);
        }
    }
    
    /**
     * 生成签名
     * 
     * @param array $params 请求参数
     * @return string
     */
    private function generateSign($params)
    {
        // 移除签名参数
        unset($params['sign']);
        
        // 按key排序
        ksort($params);
        
        // 拼接参数字符串
        $stringToBeSigned = $this->config->appSecret;
        foreach ($params as $k => $v) {
            if ($v !== '' && $v !== null) {
                $stringToBeSigned .= $k . $v;
            }
        }
        $stringToBeSigned .= $this->config->appSecret;
        
        return strtoupper(md5($stringToBeSigned));
    }
    
    /**
     * 发送HTTP请求
     * 
     * @param string $method API方法名
     * @param array $params 请求参数
     * @return array
     * @throws \Exception
     */
    private function request($method, $params = [])
    {
        $url = $this->getBaseUrl() . '/api/' . $method;
        
        // 公共参数
        $commonParams = [
            'app_key' => $this->config->appKey,
            'method' => $method,
            'timestamp' => date('Y-m-d H:i:s'),
            'format' => 'json',
            'v' => '1.0',
            'sign_method' => 'md5'
        ];
        
        // 合并参数
        $allParams = array_merge($commonParams, $params);
        
        // 生成签名
        $allParams['sign'] = $this->generateSign($allParams);
        
        try {
            $ch = curl_init();
            curl_setopt($ch, CURLOPT_URL, $url);
            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($allParams));
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
            curl_setopt($ch, CURLOPT_TIMEOUT, $this->config->timeout);
            curl_setopt($ch, CURLOPT_HTTPHEADER, [
                'Content-Type: application/x-www-form-urlencoded',
                'User-Agent: ' . $this->config->userAgent
            ]);
            
            $response = curl_exec($ch);
            $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
            $error = curl_error($ch);
            curl_close($ch);
            
            if ($error) {
                throw new \Exception('CURL错误: ' . $error);
            }
            
            if ($httpCode !== 200) {
                throw new \Exception('HTTP错误: ' . $httpCode);
            }
            
            $result = json_decode($response, true);
            if (json_last_error() !== JSON_ERROR_NONE) {
                throw new \Exception('JSON解析错误: ' . json_last_error_msg());
            }
            
            // 记录API调用日志
            $this->log('info', 'JingdongAPI Request', [
                'method' => $method,
                'params' => $allParams,
                'response' => $result
            ]);
            
            return $result;
            
        } catch (\Exception $e) {
            $this->log('error', 'JingdongAPI Error', [
                'method' => $method,
                'params' => $allParams,
                'error' => $e->getMessage()
            ]);
            throw $e;
        }
    }
    
    /**
     * 获取基础URL
     * 
     * @return string
     */
    private function getBaseUrl()
    {
        if ($this->config->sandboxMode) {
            return self::API_BASE_SANDBOX;
        } else {
            return self::API_BASE_PROD;
        }
    }
    
    /**
     * 转换坐标格式
     * 将普通坐标转换为火星坐标*10^6格式
     * 
     * @param string|float $coordinate 坐标
     * @return int
     */
    private function convertCoordinate($coordinate)
    {
        return intval(floatval($coordinate) * 1000000);
    }
    
    /**
     * 转换重量格式
     * 将重量转换为克(g)
     * 
     * @param string|float $weight 重量（kg）
     * @return int
     */
    private function convertWeight($weight)
    {
        return intval(floatval($weight) * 1000);
    }
    
    /**
     * 转换时间戳格式
     * 
     * @param string|int $time 时间（Y-m-d H:i:s 或 时间戳）
     * @return int
     */
    private function convertTimestamp($time)
    {
        if (is_numeric($time)) {
            return intval($time);
        } else {
            return strtotime($time);
        }
    }
    
    /**
     * 1. 申请成为配送商
     * 
     * @param MerchantInfo|array $merchantInfo 商户信息
     * @return ApiResult
     */
    public function applyMerchant($merchantInfo)
    {
        if (is_array($merchantInfo)) {
            $merchantInfo = new MerchantInfo($merchantInfo);
        }
        
        $merchantInfo->validate();
        
        try {
            $response = $this->request('jingdong.logistics.merchant.apply', $merchantInfo->toArray());
            return ApiResult::fromResponse($response);
        } catch (\Exception $e) {
            return ApiResult::error(-1, $e->getMessage());
        }
    }
    
    /**
     * 2. 查询配送商审核状态
     * 
     * @param string|null $applyId 申请ID
     * @return ApiResult
     */
    public function queryMerchantStatus($applyId = null)
    {
        $params = [];
        if ($applyId) {
            $params['apply_id'] = $applyId;
        }
        
        try {
            $response = $this->request('jingdong.logistics.merchant.query', $params);
            return ApiResult::fromResponse($response);
        } catch (\Exception $e) {
            return ApiResult::error(-1, $e->getMessage());
        }
    }
    
    /**
     * 3. 创建配送订单（发单）
     * 
     * @param OrderData|array $orderData 订单数据
     * @return ApiResult
     */
    public function createOrder($orderData)
    {
        if (is_array($orderData)) {
            $orderData = new OrderData($orderData);
        }
        
        $orderData->validate();
        
        // 根据真实的京东海博系统API文档构建参数
        $params = [
            // 必填参数
            'tradeOrderSource' => $orderData->tradeOrderSource ?? 1,
            'orderId' => $orderData->orderId,
            'serviceCode' => $orderData->serviceCode ?? 'STANDARD',
            'recipientName' => $orderData->recipientName,
            'recipientPhone' => $orderData->recipientPhone,
            'recipientAddress' => $orderData->recipientAddress,
            'recipientLng' => $this->convertCoordinate($orderData->recipientLng),
            'recipientLat' => $this->convertCoordinate($orderData->recipientLat),
            'prebook' => $orderData->prebook ?? 0,
            'totalWeight' => $this->convertWeight($orderData->totalWeight),
            'senderLng' => $this->convertCoordinate($orderData->senderLng),
            'senderLat' => $this->convertCoordinate($orderData->senderLat),
            'senderName' => $orderData->senderName,
            'senderContract' => $orderData->senderPhone,
            'senderAddressDetail' => $orderData->senderAddress,
            'operatorName' => $orderData->operatorName ?? $orderData->senderName,
            'carrierMerchantId' => $this->config->merchantNo,
            'extInfo' => json_encode($orderData->extInfo ?? [], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)
        ];
        
        // 添加可选参数
        $optionalFields = [
            'tradeOrderId', 'orderSequence', 'preDeliveryFee',
            'expectedDeliveryTime', 'expectedPickupTime', 'expectedLeftDeliveryTime',
            'expectedRightDeliveryTime', 'insuredMark', 'totalValue', 'totalVolume',
            'riderPickMethod', 'tipFee', 'orderRemark', 'shopRemark',
            'carrierShopId', 'carModelCode', 'riderInfo', 'category'
        ];
        
        foreach ($optionalFields as $field) {
            if (isset($orderData->$field)) {
                $value = $orderData->$field;
                
                // 特殊处理某些字段
                if (in_array($field, ['expectedDeliveryTime', 'expectedPickupTime', 'expectedLeftDeliveryTime', 'expectedRightDeliveryTime'])) {
                    $value = $this->convertTimestamp($value);
                } elseif ($field === 'goodsDetails') {
                    $value = json_encode($value, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
                } elseif ($field === 'operatorPhone') {
                    $params['operatorContract'] = $value;
                    continue;
                }
                
                $params[$field] = $value;
            }
        }
        
        // 处理物品明细
        if (isset($orderData->goodsDetails)) {
            $params['goodsDetails'] = json_encode($orderData->goodsDetails, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        }
        
        try {
            $response = $this->request('hiboos.order.create', $params);
            return ApiResult::fromResponse($response);
        } catch (\Exception $e) {
            return ApiResult::error(-1, $e->getMessage());
        }
    }
    
    /**
     * 4. 查询订单状态
     * 
     * @param string|null $outOrderId 外部订单号
     * @param string|null $jdOrderId 京东订单号
     * @return ApiResult
     */
    public function queryOrder($outOrderId = null, $jdOrderId = null)
    {
        if (empty($outOrderId) && empty($jdOrderId)) {
            return ApiResult::error(-1, '外部订单号和京东订单号至少提供一个');
        }
        
        $params = [];
        if ($outOrderId) {
            $params['out_order_id'] = $outOrderId;
        }
        if ($jdOrderId) {
            $params['jd_order_id'] = $jdOrderId;
        }
        
        try {
            $response = $this->request('jingdong.logistics.order.query', $params);
            return ApiResult::fromResponse($response);
        } catch (\Exception $e) {
            return ApiResult::error(-1, $e->getMessage());
        }
    }
    
    /**
     * 5. 取消订单
     * 
     * @param string $outOrderId 外部订单号
     * @param string $cancelReason 取消原因
     * @return ApiResult
     */
    public function cancelOrder($outOrderId, $cancelReason = '用户取消')
    {
        $params = [
            'out_order_id' => $outOrderId,
            'cancel_reason' => $cancelReason
        ];
        
        try {
            $response = $this->request('jingdong.logistics.order.cancel', $params);
            return ApiResult::fromResponse($response);
        } catch (\Exception $e) {
            return ApiResult::error(-1, $e->getMessage());
        }
    }
    
    /**
     * 6. 骑手位置查询
     * 
     * @param string $outOrderId 外部订单号
     * @return ApiResult
     */
    public function queryRiderLocation($outOrderId)
    {
        $params = [
            'out_order_id' => $outOrderId
        ];
        
        try {
            $response = $this->request('jingdong.logistics.rider.location', $params);
            return ApiResult::fromResponse($response);
        } catch (\Exception $e) {
            return ApiResult::error(-1, $e->getMessage());
        }
    }
    
    /**
     * 7. 预下单接口（获取运费）
     * 
     * @param array $orderData 订单数据
     * @return ApiResult
     */
    public function preOrder($orderData)
    {
        $params = [
            'cargo_type' => $orderData['cargo_type'] ?? '1',
            'cargo_weight' => $orderData['cargo_weight'],
            'cargo_num' => $orderData['cargo_num'] ?? '1',
            'cargo_price' => $orderData['cargo_price'],
            'pickup_info' => json_encode($orderData['pickup_info'], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
            'delivery_info' => json_encode($orderData['delivery_info'], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
            'expected_pickup_time' => $orderData['expected_pickup_time'],
            'expected_delivery_time' => $orderData['expected_delivery_time']
        ];
        
        try {
            $response = $this->request('jingdong.logistics.order.preorder', $params);
            return ApiResult::fromResponse($response);
        } catch (\Exception $e) {
            return ApiResult::error(-1, $e->getMessage());
        }
    }
    
    /**
     * 8. 查询可配送范围
     * 
     * @param string $lng 经度
     * @param string $lat 纬度
     * @return ApiResult
     */
    public function queryServiceArea($lng, $lat)
    {
        $params = [
            'longitude' => $lng,
            'latitude' => $lat
        ];
        
        try {
            $response = $this->request('jingdong.logistics.area.query', $params);
            return ApiResult::fromResponse($response);
        } catch (\Exception $e) {
            return ApiResult::error(-1, $e->getMessage());
        }
    }
    
    /**
     * 9. 查询历史订单列表
     * 
     * @param array $condition 查询条件
     * @return ApiResult
     */
    public function queryOrderList($condition = [])
    {
        $params = [
            'page_size' => $condition['page_size'] ?? 20,
            'page_no' => $condition['page_no'] ?? 1
        ];
        
        $optionalFields = ['start_time', 'end_time', 'order_status'];
        foreach ($optionalFields as $field) {
            if (isset($condition[$field])) {
                $params[$field] = $condition[$field];
            }
        }
        
        try {
            $response = $this->request('jingdong.logistics.order.list', $params);
            return ApiResult::fromResponse($response);
        } catch (\Exception $e) {
            return ApiResult::error(-1, $e->getMessage());
        }
    }
    
    /**
     * 10. 订单追加小费
     * 
     * @param string $outOrderId 外部订单号
     * @param string $tips 小费金额
     * @return ApiResult
     */
    public function addTips($outOrderId, $tips)
    {
        $params = [
            'out_order_id' => $outOrderId,
            'tips' => $tips
        ];
        
        try {
            $response = $this->request('jingdong.logistics.order.addtips', $params);
            return ApiResult::fromResponse($response);
        } catch (\Exception $e) {
            return ApiResult::error(-1, $e->getMessage());
        }
    }
    
    /**
     * 11. 订单催单
     * 
     * @param string $outOrderId 外部订单号
     * @return ApiResult
     */
    public function urgeOrder($outOrderId)
    {
        $params = [
            'out_order_id' => $outOrderId
        ];
        
        try {
            $response = $this->request('jingdong.logistics.order.urge', $params);
            return ApiResult::fromResponse($response);
        } catch (\Exception $e) {
            return ApiResult::error(-1, $e->getMessage());
        }
    }
    
    /**
     * 12. 获取订单详情
     * 
     * @param string $outOrderId 外部订单号
     * @return ApiResult
     */
    public function getOrderDetail($outOrderId)
    {
        $params = [
            'out_order_id' => $outOrderId
        ];
        
        try {
            $response = $this->request('jingdong.logistics.order.detail', $params);
            return ApiResult::fromResponse($response);
        } catch (\Exception $e) {
            return ApiResult::error(-1, $e->getMessage());
        }
    }
    
    /**
     * 13. 验证回调签名
     * 
     * @param array $data 回调数据
     * @param string $signature 签名
     * @return bool
     */
    public function verifyCallback($data, $signature)
    {
        $localSign = $this->generateSign($data);
        return $localSign === $signature;
    }
    
    /**
     * 获取配置信息
     * 
     * @return array
     */
    public function getConfig()
    {
        return [
            'app_key' => $this->config->appKey,
            'merchant_no' => $this->config->merchantNo,
            'sandbox_mode' => $this->config->sandboxMode,
            'base_url' => $this->getBaseUrl()
        ];
    }
    
    /**
     * 测试API连接
     * 
     * @return ApiResult
     */
    public function testConnection()
    {
        try {
            $result = $this->queryMerchantStatus();
            return ApiResult::success($result->data, 'API连接正常');
        } catch (\Exception $e) {
            return ApiResult::error(-1, 'API连接失败: ' . $e->getMessage());
        }
    }
}
